package entityManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

import settings.IdManager;
import settings.SavingPot;
import settings.Settings;

import dataStorage.DataHandler;
import dataStorage.DataXML;
import entity.CashFlow;
import entity.Expense;
import entity.Goal;
import entityManager.ExpenseManager.DateComparator;

/**
 * @author A0083955R Ding Sen
 * @author A0098808L Yuen Shu Hui
 */
public class GoalManager implements Manager<Goal> {
	private final DateComparator myComparator = new DateComparator();
	private ArrayList<Goal> allGoalList;
	private final String fileKey = "goal";
	private SavingPot myPot;

	public GoalManager() throws TransformerConfigurationException,
	InstantiationException, IllegalAccessException, JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException {
		loadAll();
		myPot = new SavingPot();
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public double getSavingPot() {
		return myPot.getSavedAmount();
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public double getToSaveAmt() {
		return myPot.getToSaveAmount();
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public void setSavingPot(double _savingPot) {
		myPot.setSavedAmount(_savingPot);
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public void depositSavingPot(double _amt)
	{
		myPot.IncrementSavedAmount(_amt);
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public void withdrawSavingPot(double _amt)
	{
		myPot.DecrementSavedAmount(_amt);
	}
	/**
	 * @author A0083955R Ding Sen
	 */
	public void setToSaveAmt(double toSaveAmt) {
		myPot.setToSaveAmount(toSaveAmt);
	}
	/**
	 * *Load data from DATA XML. 
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public void loadAll() throws TransformerConfigurationException,
	InstantiationException, IllegalAccessException, JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException {
		DataHandler dataHandler = DataXML.getInstance();
		allGoalList = dataHandler.load(fileKey, Goal.class);
		SortListAscendingDate(allGoalList);
	}
	/**
	 * *Add Goal.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public boolean add(Goal _target) throws TransformerConfigurationException,
	InstantiationException, IllegalAccessException, JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException {

		/*
		 * IMPORTANT
		 * 1. Check if the id.xml has the id for this object present
		 * 2. NO: create a new entry with ID = 0 <handled by IdManager>
		 */
		if(!IdManager.getInstance().isPresent(fileKey)){
			IdManager.getInstance().addLastId(fileKey);
		}
		//get new id from the IdManger
		_target.setId(IdManager.getInstance().getLastId(fileKey));

		DataHandler dataHandler = DataXML.getInstance();
		if (dataHandler.write(_target, fileKey, Goal.class)) {
			/*
			 * IMPORTANT update the ID on the id.xml file
			 */
			if (IdManager.getInstance().updateLastId(fileKey)) {
				System.out.println("Goal Write Success");
				return true;
			} else
				System.out.println("Goal Write Fail");
		} else
			System.out.println("Goal Write Fail");
		return false;
	}

	/**
	 * *Remove Goals.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public boolean remove(Goal _target) throws JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException, InstantiationException, IllegalAccessException {
		// parse the object to Expense
		DataHandler dataHandler = DataXML.getInstance();
		if (dataHandler.remove(_target, fileKey)) {
			System.out.println("Remove Success");
			return true;
		} else
			System.out.println("Remove Fail");
		return false;
	}

	/**
	 * *Update Goals.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public boolean update(Goal _target) throws IOException, JAXBException,
	TransformerConfigurationException, ParserConfigurationException,
	SAXException, TransformerException, InstantiationException, IllegalAccessException {


		DataHandler dataHandler =  DataXML.getInstance();
		if (dataHandler.update(_target.getId(), _target, fileKey, Goal.class)) {
			System.out.println("Update Success");
			return true;
		} else {
			System.out.println("Update Fail");
		}
		return false;
	}
	/**
	 * *Get the goal, by ID. 
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public Goal getById(String _id) throws TransformerConfigurationException,
	InstantiationException, IllegalAccessException, JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException {
		loadAll();
		Iterator<Goal> lt = allGoalList.iterator();
		Goal temp;

		while (lt.hasNext()) {
			temp = lt.next();
			if (temp.getId().equalsIgnoreCase(_id))
				return temp;
		}
		return null;
	}
	/**
	 * *Get All Goal List from Data XML.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public ArrayList<Goal> getAll() throws TransformerConfigurationException,
	InstantiationException, IllegalAccessException, JAXBException,
	ParserConfigurationException, SAXException, IOException,
	TransformerException {
		loadAll();
		return allGoalList;
	}
	/**
	 * *Check, and get All goals active; to prepare for calculating total amount needed later. 
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public ArrayList<Goal> getActiveGoal() throws TransformerConfigurationException, InstantiationException, IllegalAccessException, JAXBException, ParserConfigurationException, SAXException, IOException, TransformerException
	{
		ArrayList<Goal> activeGoal = new ArrayList<Goal>();
		Goal temp = null;
		loadAll();
		Iterator<Goal> lt = allGoalList.iterator();
		while(lt.hasNext())
		{
			temp = lt.next();
			if(temp.isActive() == true)
			{
				activeGoal.add(temp);
			}
		}
		return activeGoal;
	}
	/**
	 * *Check, and get by Data.  
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public ArrayList<Goal> getByDate(Calendar _fromDate, Calendar _toDate)
			throws TransformerConfigurationException, InstantiationException,
			IllegalAccessException, JAXBException,
			ParserConfigurationException, SAXException, IOException,
			TransformerException {
		loadAll();
		ArrayList<Goal> matchList = new ArrayList<Goal>();

		Iterator<Goal> it = allGoalList.iterator();
		Goal curr;

		while (it.hasNext()) {
			curr = it.next();

			if (curr.getStartDate().after(_fromDate)
					&& curr.getStartDate().before(_toDate))
				matchList.add(curr);
			else {
				Calendar.getInstance();
				if (curr.getStartDate().get(Calendar.YEAR) == _fromDate
						.get(Calendar.YEAR)
						&& curr.getStartDate().get(Calendar.MONTH) == _fromDate
						.get(Calendar.MONTH)
						&& curr.getStartDate().get(Calendar.DATE) == _fromDate
						.get(Calendar.DATE))
					matchList.add(curr);
				else if (curr.getStartDate().get(Calendar.YEAR) == _toDate
						.get(Calendar.YEAR)
						&& curr.getStartDate().get(Calendar.MONTH) == _toDate
						.get(Calendar.MONTH)
						&& curr.getStartDate().get(Calendar.DATE) == _toDate
						.get(Calendar.DATE))
					matchList.add(curr);
			}
		}
		return matchList;
	}
	/**
	 * *Get The goal By Keyword.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public ArrayList<Goal> getByKeyword(String _keyword)
			throws TransformerConfigurationException, InstantiationException,
			IllegalAccessException, JAXBException,
			ParserConfigurationException, SAXException, IOException,
			TransformerException {
		loadAll();
		ArrayList<Goal> matchList = new ArrayList<Goal>();

		Iterator<Goal> it = allGoalList.iterator();
		Goal curr;
		while (it.hasNext()) {
			curr = it.next();
			if (curr.getName().toLowerCase().indexOf(_keyword.toLowerCase()) != -1) {
				matchList.add(curr);
			}
		}
		return matchList;
	}

	/**
	 * Calculate total amount saved in goals by the input goal list
	 * @author A0098808L Yuen Shu Hui
	 * @param _inputList
	 * @return
	 */
	public double CalSavedAmount(ArrayList<Goal> _inputList)
	{
		double sum = 0;
		Iterator<Goal> lt = _inputList.iterator();
		while(lt.hasNext())
		{
			sum += lt.next().getAmountAchieved();
		}
		return sum;
	}

	/**
	 * Calculate total amount require by the input goal list
	 * @author A0098808L Yuen Shu Hui
	 * @param _inputList
	 * @return
	 */
	public double CalculateSumOfGoal(ArrayList<Goal> _inputList)
	{
		double sum = 0;
		Iterator<Goal> lt = _inputList.iterator();
		while(lt.hasNext())
		{
			sum += lt.next().getRequireAmount();
		}
		return sum;
	}

	/**
	 * Deactivate all the goal
	 * @author A0098808L Yuen Shu Hui
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public void deactivateAllGoal() throws TransformerConfigurationException, InstantiationException, IllegalAccessException, IOException, JAXBException, ParserConfigurationException, SAXException, TransformerException
	{
		Iterator<Goal> lt = this.getAll().iterator();
		Goal curr;
		while(lt.hasNext())
		{
			curr = lt.next();
			curr.setActive(false);
			this.update(curr);
		}
	}

	/**
	 * *Upon the Users Editting the Goal, Re-check Achieved.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public void checkUpdate(Goal updatedGoal) throws TransformerConfigurationException, InstantiationException, IllegalAccessException, IOException, JAXBException, ParserConfigurationException, SAXException, TransformerException 
	{
		if (updatedGoal.getTotalAmount()>updatedGoal.getAmountAchieved()) 
			updatedGoal.setAchieved(false);

		updatedGoal.calculateRequireAmt(Calendar.getInstance());
		this.update(updatedGoal); 

	}
	/**
	 * *Each Time, activate all goals, if needed.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */
	public void ActivateDisableGoal(Calendar today) throws TransformerConfigurationException, InstantiationException, IllegalAccessException, IOException, JAXBException, ParserConfigurationException, SAXException, TransformerException
	{
		ArrayList<Goal> goalList = new ArrayList<Goal>();
		Goal curr;
		Iterator<Goal> lt = this.getAll().iterator();
		while(lt.hasNext())
		{
			curr = lt.next();
			if(curr.isAchieved() != true)
			{
				curr.calculateRequireAmt(today);
				goalList.add(curr);
				this.update(curr);
			}
		}

		if(goalList.size() > 0 && myPot.getSavedAmount() > this.CalculateSumOfGoal(goalList))
		{
			lt = goalList.iterator();
			while(lt.hasNext())
			{
				curr = lt.next();
				if(curr.isActive() == false)
				{
					curr.setActive(true);

					this.update(curr);
				}
			}
		}
	}


	/**
	 * Handle distribution of fund from saving pot into each goal, if there is sufficient fund
	 * @author A0098808L Yuen Shu Hui
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws TransformerException
	 */
	public void HandleGoal() throws TransformerConfigurationException, InstantiationException, IllegalAccessException, JAXBException, ParserConfigurationException, SAXException, IOException, TransformerException
	{
		ArrayList<Goal> activeGoal = this.getActiveGoal();

		if( activeGoal.size() > 0)
		{
			Goal temp;
			Iterator<Goal> lt = activeGoal.iterator();

			while(lt.hasNext())	//distribute fund
			{
				temp = lt.next();
				double addingAmt = new Double(Settings.doubleTo2DP(temp.getRequireAmount()));// * percentage));

				if(temp.isAchieved() == false){
					if(addingAmt < myPot.getSavedAmount())
					{
						temp.depositToGoal(addingAmt);
						this.withdrawSavingPot(addingAmt);

						if(temp.getAmountAchieved() >= temp.getTotalAmount())
						{
							temp.setActive(false);
							temp.setAchieved(true);
							double extra = temp.getAmountAchieved() - temp.getTotalAmount();
							this.depositSavingPot(extra);
							temp.setAmountAchieved(temp.getTotalAmount());
							System.out.println(temp.getId() + " " + temp.getName() + " have achieved");
						}
						this.update(temp);
					}
					else
					{
						System.out.println("ERROR!!!! INSUFFICIENT FUND");
						temp.setActive(false);
						this.update(temp);
					}
				}
			}// end of distribution fund-while
		} // end of first check(activeList != null)
	}
	/**
	 * *Sort In AscendingDate; Earliest Goals get priority.
	 * @author A0083955R Ding Sen
	 * @throws TransformerConfigurationException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IOException
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws TransformerException
	 */

	public void SortListAscendingDate(ArrayList<Goal> _list)
	{
		Collections.sort(_list, myComparator);
	}
	
	class DateComparator implements Comparator<Goal>{
		public int compare(Goal obj1, Goal obj2){   
			Date a = (obj1).getStartDate().getTime();    
			Date b = (obj2).getStartDate().getTime();  

			if(a.before(b))
				return -1;
			else if(a.after(b))
				return 1;
			else
				return 0;
		}
	}

}
